// Promise只有三种状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    constructor(executor) {
        // 构造函数中执行传入的执行器
        // try catch获取执行器的异常 使用本promise的reject处理
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }

    // 初始化
    status = PENDING
    value = undefined
    error = undefined
    successCallbackList = []
    failCallbackList = []

    // resolve将promise的状态变为fulfilled 且不可逆
    // 使用者通过在异步回调中使用它来保证异步调用
    resolve = value => { // 不能是普通函数: 因为不是由promise.resolve这样调用的
        if (this.status !== PENDING) return
        this.status = FULFILLED
        this.value = value

        // 执行异步前缓存的回调函数
        // 并且一个promise可能有多个then函数调用, 所以回调函数是一个数组
        while(this.successCallbackList.length) this.successCallbackList.shift()()
    }

    // reject将promise的状态变为rejected 且不可逆
    // 使用者通过在异步回调中使用它来保证异步调用
    reject = error => {
        if (this.status !== PENDING) return
        this.status = REJECTED
        this.error = error
        // if (this.failCallback) this.failCallback(error)
  
        // 执行异步前缓存的回调函数
        // 并且一个promise可能有多个then函数调用, 所以回调函数是一个数组
        while(this.failCallbackList.length) this.failCallbackList.shift()(error)
    }

    // then函数接收两个回调函数, 返回一个新的promise对象
    // 传入的回调函数会返回一个值: 返回值可能是一个普通值, 也可能是一个promise对象
    // 对于普通值, 我们通过resolve将它传给下一个then的回调函数, 返回的promise状态为fulfilled
    // 对于promise对象, 我们要获取它的执行结果, 以这个promise对象的状态为我们then反回的新promise对象的状态
    then (onSuccess, onFail) { // 同一个promise对象的then方法可以被调很多次
        onSuccess = onSuccess || (value => value) // 兼容没有传入成功回调
        onFail = onFail || (error => { throw error }) // 兼容没有传入失败回调
        // const that = this
        let promise = new MyPromise((resolve, reject) => {
            // console.log(that === this) // 这个很关键~
            if (this.status === FULFILLED) {
                setTimeout(() => { 
                    // 这里直接获取的promise是不行的, 因为执行器是在Promise构造器里执行的
                    // 它执行的时候我们new的这个promise对象还不存在
                    // 解决方式就利用setTimeout将这里面的操作变成一个异步宏任务
                    try { // 捕获resolvePromise函数执行的异常
                        resolvePromise(onSuccess(this.value), resolve, reject, promise)
                    } catch (e) {
                        console.log(e)
                        reject(e)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                // onfail的处理是一样的
                setTimeout(() => {
                    try {
                        resolvePromise(onFail(this.error), resolve, reject, promise)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            } else { // 处理异步的情况 pending
                // 缓存回调函数
                this.successCallbackList.push(() => {
                    setTimeout(() => {
                        try {
                            resolvePromise(onSuccess(this.value), resolve, reject, promise)
                        } catch (e) {
                            reject(e)
                        }                    
                    }, 0)
                })
                this.failCallbackList.push(() => {
                    setTimeout(() => {
                        try {
                            resolvePromise(onFail(this.error), resolve, reject, promise)
                        } catch (e) {
                            reject(e)
                        }                    
                    }, 0)
                })
                // this.failCallbackList.push(onFail)
            }
        })

        return promise
        // return this // 为什么不能return this?
    }

    // 加入一步then函数, 执行一个onFail, 如无异常则走默认onSuccess直接过
    catch (callback) {
        return this.then(undefined, callback)
    }

    // 不论成功或失败 始终都会执行这个callback
    // 后面可以链式调用then()
    // callback可以返回普通值或Promise对象 但会忽略它们的状态 继续返回finally之前传入的状态
    // finally的callback中, 是拿不到promise的执行结果的
    finally (callback) {
        return this.then(res => {
            return MyPromise.resolve(callback()).then(() => res)
        }, error => {
            return MyPromise.resolve(callback()).then(() => { throw error })
        })
    }

    // 传入的数组中可以有promise对象和普通值, 返回一个新的promise对象(可以链式调用)
    // 普通值直接放入result数组
    // promise对象需要等它执行完后, 将结果存入result
    static all (array) {
        let result = []
        let index = 0
        return new MyPromise((resolve, reject) => {
            function addData (key, value) {
                result[key] = value
                index++
                if (index === array.length) { // 等所有promise对象都调用了addData, 说明所有异步promise都已完成
                    resolve(result)
                }
            }
            array.forEach((item, index) => {
                if (item instanceof MyPromise) {
                    // promise 对象
                    item.then(value => addData(index, value), error => reject(error))
                } else { // 普通值
                    addData(index, item)
                }
            })
            // resolve(result)
        })
    }

    // 静态resolve可以接收一个普通值或一个promise对象
    // 如果是promise, 则返回它
    // 如果是普通值, 则把他promise化后返回一个包含这个普通值promise对象
    static resolve (value) {
        if (value instanceof MyPromise) {
            return value
        } else {
            return new MyPromise(resolve => resolve(value))
        }
    }
}

function resolvePromise(x, resolve, reject, promise) {
    if (x === promise) {
        // 如果promise返回了自己 那么抛出异常
        return reject(new TypeError('cycle detected for promise'))
    }
    if (x instanceof MyPromise) {
        // 如果callback返回的是一个新的promise对象 
        // 那么then返回的那个promise对象的状态和结果值
        // 会变成callback这个promise对象的状态和结果值 (成功/失败)
        x.then(resolve, reject)
    } else {
        // 如果callback返回的是一个普通值
        // 那么then返回的promise对象状态为成功, 结果为这个普通值 
        resolve(x)
    }
}

module.exports = MyPromise